import os
import shutil
import json
import platform
import sys
import subprocess
from driver import Initializer
from driver import Builder
from util import *
from option import Options


class Initializer_cpp(Initializer):
    def __init__(self, options):
        self.options = options

    def check(self):
        if not os.path.exists("repo.init"):
            return self.init()
        else:
            return True

    def build_backend(self):
        os.chdir("rpc-backend-cpp")
        file = open("CMakeLists.txt", "w")
        file.write("cmake_minimum_required(VERSION 3.5)\n")
        if platform.system() == "Windows":
            file.write("set(CMAKE_CXX_STANDARD 17)\n")
        file.write("project (rpc-backend-cpp)\n")
        file.write("SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../lib/root)\n")
        file.write('INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/coroutine)\n')
        if platform.system() == "Windows":
            file.write(
                'set(SRC_LIST src/rpc.cpp src/rpc_proxy.cpp src/rpc_logger.cpp src/rpc_stub.cpp '
                'src/rpc_statistics_impl.cpp src/coroutine/detail/timer.cpp src/coroutine/detail/coro_call.cpp '
                'src/coroutine/windows/coroutine.cpp)\n')
        else:
            file.write(
                'set(SRC_LIST src/rpc.cpp src/rpc_proxy.cpp src/rpc_logger.cpp src/rpc_stub.cpp '
                'src/rpc_statistics_impl.cpp src/coroutine/detail/timer.cpp src/coroutine/detail/coro_call.cpp '
                'src/coroutine/linux/coroutine.cpp)\n')
        file.write('add_library(rpc ${SRC_LIST})\n')
        file.write('SET_TARGET_PROPERTIES(rpc PROPERTIES PREFIX "lib")\n')
        file.write('SET_TARGET_PROPERTIES(rpc PROPERTIES SUFFIX ".a")\n')
        file.write("IF (MSVC)\n")
        file.write(
            'set_target_properties(rpc PROPERTIES COMPILE_FLAGS "/EHa /wd4267 /wd4311 /wd4302 /wd4312 /wd4244 '
            '/wd4065")\n')
        file.write('    foreach(var\n')
        file.write('        CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n')
        file.write('        CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO\n')
        file.write('        CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE\n')
        file.write('        CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO\n')
        file.write('    )\n')
        file.write('        if(${var} MATCHES "/MD")\n')
        file.write('            string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")\n')
        file.write('        endif()\n')
        file.write('    endforeach()\n')
        file.write("ELSE()\n")
        file.write('    if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")\n')
        file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                             "machine") + ' -g -O2 -std=c++17 -Wall -fpermissive -fPIC")\n')
        file.write('        set(CMAKE_CXX_COMPILER "clang++")\n')
        file.write('    elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")\n')
        file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                             "machine") + ' -g -O2 -std=c++17 -Wall -fpermissive -fPIC")\n')
        file.write('        set(CMAKE_CXX_COMPILER "g++")\n')
        file.write('    endif()\n')
        file.write("ENDIF ()\n")
        file.close()
        if platform.system() == "Windows":
            os.system('cmake -G  "Visual Studio 16 2019" -A x64')
            os.system('cmake --build . --config "Release"')
            os.system('cmake --build . --config "Debug"')
        else:
            os.system("cmake .")
            os.system("make")
        shutil.copyfile("src/cppgen.py", "../../bin/cppgen.py")
        shutil.copyfile("src/cppgen_pb_layer.py", "../../bin/cppgen_pb_layer.py")
        shutil.copyfile("src/rpc_root.h", "../../src/include/root/rpc_root.h")
        shutil.copyfile("src/object_pool.h", "../../src/include/root/object_pool.h")
        shutil.copyfile("src/rpc_defines.h", "../../src/include/root/rpc_defines.h")
        shutil.copyfile("src/rpc_logger.h", "../../src/include/root/rpc_logger.h")
        shutil.copyfile("src/rpc_proxy.h", "../../src/include/root/rpc_proxy.h")
        shutil.copyfile("src/rpc_service.h", "../../src/include/root/rpc_service.h")
        shutil.copyfile("src/rpc_singleton.h", "../../src/include/root/rpc_singleton.h")
        shutil.copyfile("src/rpc_stub.h", "../../src/include/root/rpc_stub.h")
        shutil.copyfile("src/rpc_transport.h", "../../src/include/root/rpc_transport.h")
        shutil.copyfile("src/rpc_statistics.hh", "../../src/include/root/rpc_statistics.hh")
        shutil.copyfile("src/coroutine/coroutine.h", "../../src/include/root/coroutine/coroutine.h")
        shutil.copyfile("src/coroutine/detail/list.h", "../../src/include/root/coroutine/detail/list.h")
        shutil.copyfile("src/coroutine/detail/timer.h", "../../src/include/root/coroutine/detail/timer.h")
        shutil.copyfile("src/rpc_interface_descriptor.hh", "../../src/include/root/rpc_interface_descriptor.hh")
        shutil.copyfile("src/rpc_probe.h", "../../src/include/root/rpc_probe.h")
        if platform.system() == "Windows":
            shutil.copyfile("src/coroutine/windows/coroutine.h", "../../src/include/root/coroutine/windows/coroutine.h")
        else:
            shutil.copyfile("src/coroutine/linux/coroutine.h", "../../src/include/root/coroutine/linux/coroutine.h")
        os.chdir("..")

    def checkPrecondition(self):
        if not check_pb_env():
            print("Invalid protobuffer environment!")
            return False

        if not check_git_env():
            return False

        if not check_compiler_env():
            return False

        return True

    def inspectpath(self):
        if not os.path.exists("tmp"):
            os.mkdir("tmp")
        if not os.path.exists("bin"):
            os.mkdir("bin")
        if not os.path.exists("usr/impl"):
            os.makedirs("usr/impl")
        if not os.path.exists("lib/root"):
            os.makedirs("lib/root")
        if not os.path.exists("lib/proxy"):
            os.makedirs("lib/proxy")
        if not os.path.exists("lib/stub"):
            os.makedirs("lib/stub")
        if not os.path.exists("src/idl"):
            os.makedirs("src/idl")
        if not os.path.exists("src/include/root/coroutine/detail"):
            os.makedirs("src/include/root/coroutine/detail")
        if platform.system() == "Windows":
            if not os.path.exists("src/include/root/coroutine/windows"):
                os.makedirs("src/include/root/coroutine/windows")
        else:
            if not os.path.exists("src/include/root/coroutine/linux"):
                os.makedirs("src/include/root/coroutine/linux")
        if not os.path.exists("src/proxy"):
            os.makedirs("src/proxy")
        if not os.path.exists("src/stub"):
            os.makedirs("src/stub")


    def init(self):
        print("Initializing repo...")
        if not self.checkPrecondition():
            return False

        self.inspectpath()

        if os.path.exists("tmp"):
            os.chdir("tmp")
        if os.path.exists("rpc-frontend"):
            os.chdir("rpc-frontend")
            if os.system("git pull") != 0:
                return False
            os.chdir("..")
        else:
            if os.system("git clone https://gitee.com/dennis-kk/rpc-frontend") != 0:
                print("git clone https://gitee.com/dennis-kk/rpc-frontend failed")
                return False
        self.build_frontend()
        os.chdir("..")
        os.chdir("tmp")
        if os.path.exists("rpc-backend-cpp"):
            os.chdir("rpc-backend-cpp")
            if os.system("git pull") != 0:
                return False
            os.chdir("..")
        else:
            if os.system("git clone https://gitee.com/dennis-kk/rpc-backend-cpp") != 0:
                print("git clone https://gitee.com/dennis-kk/rpc-backend-cpp failed")
                return False
        self.build_backend()
        os.chdir("..")
        open("repo.init", "w+").close()
        return True

    def checkEnv(self):
        os.system("protoc --version")
        os.system("git --version")
        os.system("cmake --version")
        if platform.system() == "Linux":
            if os.system("gcc --version") != 0:
                os.system("clang --version")
        else:
            print("Visual Studio 16 2019")


class Builder_cpp(Builder):
    def __init__(self, options):
        Builder.__init__(self, options)

    def list_all_dir(self, rootdir):
        _dirs = []
        list = os.listdir(rootdir)
        for i in range(0, len(list)):
            path = rootdir + '/' + list[i]
            if os.path.isdir(path):
                _dirs.append(path)
                _dirs.extend(self.list_all_dir(path))
        return _dirs

    def build_idl(self, name, sname=None):
        idl_name = self.check_idl_name(name)
        if not os.path.exists("src/idl/" + idl_name):
            print (name + " not found in repo.")
            return
        (base_name, _) = os.path.splitext(os.path.basename(idl_name))
        if not os.path.exists("tmp/" + base_name + "/proxy"):
            os.makedirs("tmp/" + base_name + "/proxy")
        if not os.path.exists("tmp/" + base_name + "/stub"):
            os.makedirs("tmp/" + base_name + "/stub")
        if not os.path.exists("lib/" + base_name):
            os.makedirs("lib/" + base_name)
        if not os.path.exists("lib/proxy/" + base_name):
            os.makedirs("lib/proxy/" + base_name)
        if not os.path.exists("lib/stub/" + base_name):
            os.makedirs("lib/stub/" + base_name)
        json_str = json.load(open("src/idl/" + base_name + ".idl.cpp.json"))
        for service_name in json_str["serviceNames"]:
            if not os.path.exists("tmp/" + base_name + "/proxy/" + service_name):
                os.makedirs("tmp/" + base_name + "/proxy/" + service_name)
            if not os.path.exists("tmp/" + base_name + "/stub/" + service_name):
                os.makedirs("tmp/" + base_name + "/stub/" + service_name)
        os.chdir("tmp/" + base_name)
        file = open("CMakeLists.txt", "w")
        file.write("cmake_minimum_required(VERSION 3.5)\n")
        if platform.system() == "Windows":
            file.write("set(CMAKE_CXX_STANDARD 17)\n")
        file.write("project (common_pb_layer)\n")
        if platform.system() == "Windows":
            file.write("if (MSVC)\n")
            file.write('    set(CMAKE_PREFIX_PATH "' + self.options.get_option("cpp",
                                                                               "protobuf_path") + '" "${CMAKE_PREFIX_PATH}")\n')
            file.write("endif()\n")
        file.write('find_package(Protobuf REQUIRED)\n')
        file.write('include_directories(${Protobuf_INCLUDE_DIRS})\n')
        file.write("SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../lib/" + base_name + ")\n")
        file.write(
            'INCLUDE_DIRECTORIES(/usr/include/ ${PROJECT_SOURCE_DIR}/../../src/include/' + base_name + '/protobuf/)\n')
        file.write(
            'set(SRC_LIST ${PROJECT_SOURCE_DIR}/../../src/' + base_name + '/protobuf/' + base_name + '.service.pb.cc)\n')
        file.write('add_library(' + base_name + ' ${SRC_LIST})\n\n')
        file.write("SET_TARGET_PROPERTIES(" + base_name + ' PROPERTIES PREFIX "lib")\n')
        file.write("SET_TARGET_PROPERTIES(" + base_name + ' PROPERTIES SUFFIX ".a")\n')
        file.write("IF (MSVC)\n")
        file.write(
            '    set_target_properties(' + base_name + ' PROPERTIES COMPILE_FLAGS "/EHa /bigobj /wd4267 /wd4311 /wd4302 /wd4312 /wd4244 /wd4065 /wd4250")\n')
        file.write('    foreach(var\n')
        file.write('        CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n')
        file.write('        CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO\n')
        file.write('        CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE\n')
        file.write('        CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO\n')
        file.write('    )\n')
        file.write('        if(${var} MATCHES "/MD")\n')
        file.write('            string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")\n')
        file.write('        endif()\n')
        file.write('    endforeach()\n')
        file.write("ELSE()\n")
        file.write('    if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")\n')
        file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                             "machine") + ' -g -O2 -std=c++17 -Wall -fnon-call-exceptions -fPIC")\n')
        file.write('        set(CMAKE_CXX_COMPILER "clang++")\n')
        file.write('    elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")\n')
        file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                             "machine") + ' -g -O2 -std=c++17 -Wall -fnon-call-exceptions -fPIC")\n')
        file.write('        set(CMAKE_CXX_COMPILER "g++")\n')
        file.write('    endif()\n')
        file.write("ENDIF ()\n")
        file.close()
        if platform.system() == "Windows":
            os.system('cmake -G  "Visual Studio 16 2019" -A x64')
            os.system('cmake --build . --config "Release"')
            os.system('cmake --build . --config "Debug"')
        else:
            os.system("cmake .")
            os.system("make")
        all_proxy_a_path = []
        all_proxy_a_path_debug = []
        all_proxy_a_path_release = []
        for service in json_str["services"]:
            if sname is not None:
                if sname != service["name"]:
                    continue
            os.chdir("proxy/" + service["name"])
            file = open("CMakeLists.txt", "w")
            file.write("cmake_minimum_required(VERSION 3.5)\n")
            if platform.system() == "Windows":
                file.write("set(CMAKE_CXX_STANDARD 17)\n")
            file.write("project (service_proxy)\n")
            if platform.system() == "Windows":
                file.write("if (MSVC)\n")
                file.write('    set(CMAKE_PREFIX_PATH "' + self.options.get_option("cpp",
                                                                                   "protobuf_path") + '"	"${CMAKE_PREFIX_PATH}")\n')
                file.write("endif()\n")
            file.write('find_package(Protobuf REQUIRED)\n')
            file.write('include_directories(${Protobuf_INCLUDE_DIRS})\n')
            file.write(
                "SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../../../lib/proxy/" + base_name + "/" + service[
                    "name"] + ")\n")
            all_proxy_a_path_debug.append(
                '${PROJECT_SOURCE_DIR}/../../../../lib/proxy/' + base_name + "/" + service["name"] + '/Debug/lib' +
                service["name"] + '_proxy.a')
            all_proxy_a_path_release.append(
                '${PROJECT_SOURCE_DIR}/../../../../lib/proxy/' + base_name + "/" + service["name"] + '/Release/lib' +
                service["name"] + '_proxy.a')
            all_proxy_a_path.append(
                '${PROJECT_SOURCE_DIR}/../../../../lib/proxy/' + base_name + "/" + service["name"] + '/lib' + service[
                    "name"] + '_proxy.a')
            file.write(
                'INCLUDE_DIRECTORIES(/usr/include/ ${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + ' ${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + '/' +
                service[
                    "name"] + '/proxy ${PROJECT_SOURCE_DIR}/../../../../src/include/root ${PROJECT_SOURCE_DIR}/../../../../src/include/root/coroutine ' + '${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + '/protobuf)\n')
            file.write('set(SRC_LIST ${PROJECT_SOURCE_DIR}/../../../../src/proxy/' + base_name + '/' + service[
                "name"] + '/' + base_name + '.service.' + service[
                           "name"] + '.proxy.cpp ' + '${PROJECT_SOURCE_DIR}/../../../../src/proxy/' + base_name + '/' +
                       service["name"] + '/' + base_name + '.service.' + service[
                           "name"] + '.proxy.serializer.cpp' + ')\n')
            file.write('add_library(' + service["name"] + '_proxy ' + ' ${SRC_LIST})\n\n')
            file.write("SET_TARGET_PROPERTIES(" + service["name"] + '_proxy ' + ' PROPERTIES PREFIX "lib")\n')
            file.write("SET_TARGET_PROPERTIES(" + service["name"] + '_proxy ' + ' PROPERTIES SUFFIX ".a")\n')
            file.write("IF (MSVC)\n")
            file.write('    set_target_properties(' + service[
                "name"] + '_proxy ' + ' PROPERTIES COMPILE_FLAGS "/EHa /bigobj /wd4267 /wd4311 /wd4302 /wd4312 /wd4244 /wd4065 /wd4250")\n')
            file.write('    foreach(var\n')
            file.write('        CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n')
            file.write('        CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO\n')
            file.write('        CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE\n')
            file.write('        CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO\n')
            file.write('        )\n')
            file.write('        if(${var} MATCHES "/MD")\n')
            file.write('            string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")\n')
            file.write('        endif()\n')
            file.write('    endforeach()\n')
            file.write("ELSE()\n")
            file.write('    if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")\n')
            file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                                 "machine") + ' -g -O2 -std=c++17 -Wall -Wno-deprecated-declarations -fnon-call-exceptions -fPIC")\n')
            file.write('        set(CMAKE_CXX_COMPILER "clang++")\n')
            file.write('    elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")\n')
            file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                                 "machine") + ' -g -O2 -std=c++17 -Wall -Wno-deprecated-declarations -fnon-call-exceptions -fPIC")\n')
            file.write('        set(CMAKE_CXX_COMPILER "g++")\n')
            file.write('    endif()\n')
            file.write("ENDIF ()\n")
            file.close()
            if platform.system() == "Windows":
                os.system('cmake -G  "Visual Studio 16 2019" -A x64')
                os.system('cmake --build . --config "Release"')
                os.system('cmake --build . --config "Debug"')
            else:
                os.system("cmake .")
                os.system("make")
            os.chdir("../../")
        for service in json_str["services"]:
            if sname is not None:
                if sname != service["name"]:
                    continue
            os.chdir("stub/" + service["name"])
            if self.options.get_option("cpp", "custom_cmake") == "true":
                if not os.path.exists("default"):
                    os.makedirs("default")
                os.chdir("default")
                file = open("CMakeLists.txt", "w")
                file.write("cmake_minimum_required(VERSION 3.5)\n")
                if platform.system() == "Windows":
                    file.write("set(CMAKE_CXX_STANDARD 17)\n")
                file.write("project (service_stub)\n")
                file.write("if (MSVC)\n")
                file.write('    set(CMAKE_PREFIX_PATH "' + self.options.get_option("cpp",
                                                                                   "protobuf_path") + '"	"${CMAKE_PREFIX_PATH}")\n')
                file.write("endif()\n")
                file.write('find_package(Protobuf REQUIRED)\n')
                file.write('include_directories(${Protobuf_INCLUDE_DIRS})\n')
                file.write("SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../../../../lib/stub/" + base_name + "/" +
                           service["name"] + ")\n")
                file.write(
                    'INCLUDE_DIRECTORIES(/usr/include/ ${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + ' ${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + '/' +
                    service[
                        "name"] + '/stub ${PROJECT_SOURCE_DIR}/../../../../src/include/root ${PROJECT_SOURCE_DIR}/../../../../src/include/root/coroutine ' + '${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + '/protobuf/ ${PROJECT_SOURCE_DIR}/../../../../usr/impl/' + base_name + '/' +
                    service["name"] + ')\n')
                file.write('set(SRC_LIST ${PROJECT_SOURCE_DIR}/../../../../../src/stub/' + base_name + '/' + service[
                    "name"] + '/' + base_name + '.service.' + service[
                               "name"] + '.stub.cpp ' + '${PROJECT_SOURCE_DIR}/../../../../../src/stub/' + base_name + '/' +
                           service["name"] + '/' + base_name + '.service.' + service[
                               "name"] + '.stub.serializer.cpp' + ')\n')
                file.write('add_library(' + service["name"] + '_stub ' + ' ${SRC_LIST})\n\n')
                file.write("SET_TARGET_PROPERTIES(" + service["name"] + '_stub ' + ' PROPERTIES PREFIX "lib")\n')
                file.write("SET_TARGET_PROPERTIES(" + service["name"] + '_stub ' + ' PROPERTIES SUFFIX ".a")\n')
                file.write("IF (MSVC)\n")
                file.write('    set_target_properties(' + service[
                    "name"] + '_stub ' + ' PROPERTIES COMPILE_FLAGS "/EHa /bigobj /wd4267 /wd4311 /wd4302 /wd4312 /wd4244 /wd4065 /wd4250")\n')
                file.write('    foreach(var\n')
                file.write('        CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n')
                file.write('        CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO\n')
                file.write('        CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE\n')
                file.write('        CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO\n')
                file.write('        )\n')
                file.write('        if(${var} MATCHES "/MD")\n')
                file.write('            string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")\n')
                file.write('        endif()\n')
                file.write('    endforeach()\n')
                file.write("ELSE()\n")
                file.write('    if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")\n')
                file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                                     "machine") + ' -g -O2 -std=c++17 -Wall -Wno-deprecated-declarations -fnon-call-exceptions -fPIC")\n')
                file.write('        set(CMAKE_CXX_COMPILER "clang++")\n')
                file.write('    elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")\n')
                file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                                     "machine") + ' -g -O2 -std=c++17 -Wall -Wno-deprecated-declarations -fnon-call-exceptions -fPIC")\n')
                file.write('        set(CMAKE_CXX_COMPILER "g++")\n')
                file.write('    endif()\n')
                file.write("ENDIF ()\n")
                file.close()
                if platform.system() == "Windows":
                    os.system('cmake -G  "Visual Studio 16 2019" -A x64')
                    os.system('cmake --build . --config "Release"')
                    os.system('cmake --build . --config "Debug"')
                else:
                    os.system("cmake .")
                    os.system("make")
                os.chdir("..")
                if os.path.exists("CMakeLists.txt"):
                    os.system("cmake .")
                    os.system("make")
                else:
                    print ("User given CMakeLists.txt not found for service: " + service["name"])
            else:
                file = open("CMakeLists.txt", "w")
                file.write("cmake_minimum_required(VERSION 3.5)\n")
                if platform.system() == "Windows":
                    file.write("set(CMAKE_CXX_STANDARD 17)\n")
                file.write("project (service_stub)\n")
                if platform.system() == "Windows":
                    file.write("if (MSVC)\n")
                    file.write('set(CMAKE_PREFIX_PATH "' + self.options.get_option("cpp",
                                                                                   "protobuf_path") + '"	"${CMAKE_PREFIX_PATH}")\n')
                    file.write('set(PROTOBUF_PATH "' + self.options.get_option("cpp", "protobuf_path") + '")\n')
                    file.write("endif()\n")
                file.write('find_package(Protobuf REQUIRED)\n')
                file.write('include_directories(${Protobuf_INCLUDE_DIRS})\n')
                file.write(
                    "SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../../../lib/stub/" + base_name + "/" + service[
                        "name"] + ")\n")
                file.write(
                    'INCLUDE_DIRECTORIES(/usr/include/ ${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + ' ${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + '/' +
                    service[
                        "name"] + '/stub ${PROJECT_SOURCE_DIR}/../../../../src/include/root ${PROJECT_SOURCE_DIR}/../../../../src/include/root/coroutine ' + '${PROJECT_SOURCE_DIR}/../../../../src/include/' + base_name + '/protobuf/ ${PROJECT_SOURCE_DIR}/../../../../usr/impl/' + base_name + '/' +
                    service[
                        "name"] + ' ${PROJECT_SOURCE_DIR}/../../../../../box/ ' + ' ${PROJECT_SOURCE_DIR}/../../../../../config/ ' + ' ${PROJECT_SOURCE_DIR}/../../../../../scheduler/ ' + ' ${PROJECT_SOURCE_DIR}/../../../../../redis/ ' + ' ${PROJECT_SOURCE_DIR}/../../../../../http/ ' + ' ${PROJECT_SOURCE_DIR}/../../../../../command/ ' + ' ${PROJECT_SOURCE_DIR}/../../../../../argument/ ' + ' ${PROJECT_SOURCE_DIR}/../../../../../time/ ' + ')\n')
                file.write('set(SRC_LIST ${PROJECT_SOURCE_DIR}/../../../../src/stub/' + base_name + '/' + service[
                    "name"] + '/' + base_name + '.service.' + service[
                               "name"] + '.stub.cpp ' + '${PROJECT_SOURCE_DIR}/../../../../src/stub/' + base_name + '/' +
                           service["name"] + '/' + base_name + '.service.' + service[
                               "name"] + '.stub.serializer.cpp' + ')\n')
                file.write('add_library(' + service["name"] + '_stub ' + ' ${SRC_LIST})\n\n')
                file.write("IF (MSVC)\n")
                file.write('    set_target_properties(' + service[
                    "name"] + '_stub ' + ' PROPERTIES COMPILE_FLAGS "/EHa /bigobj /wd4267 /wd4065 /wd4250")\n')
                file.write('    foreach(var\n')
                file.write('        CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n')
                file.write('        CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO\n')
                file.write('        CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE\n')
                file.write('        CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO\n')
                file.write('        )\n')
                file.write('        if(${var} MATCHES "/MD")\n')
                file.write('            string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")\n')
                file.write('        endif()\n')
                file.write('    endforeach()\n')
                file.write("ELSE()\n")
                file.write('    if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")\n')
                file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                                     "machine") + ' -g -O2 -std=c++17 -Wall -Wno-deprecated-declarations -fnon-call-exceptions -fPIC")\n')
                file.write('        set(CMAKE_CXX_COMPILER "clang++")\n')
                file.write('    elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")\n')
                file.write('        SET(CMAKE_CXX_FLAGS "' + self.options.get_option("cpp",
                                                                                     "machine") + ' -g -O2 -std=c++17 -Wall -Wno-deprecated-declarations -fnon-call-exceptions -fPIC")\n')
                file.write('        set(CMAKE_CXX_COMPILER "g++")\n')
                file.write('    endif()\n')
                file.write("ENDIF ()\n")
                file.write("SET_TARGET_PROPERTIES(" + service["name"] + '_stub ' + ' PROPERTIES PREFIX "lib")\n')
                file.write("SET_TARGET_PROPERTIES(" + service["name"] + '_stub ' + ' PROPERTIES SUFFIX ".a")\n')
                for d in self.list_all_dir("../../../../usr/impl/" + base_name + "/" + service["name"]):
                    file.write("aux_source_directory(${PROJECT_SOURCE_DIR}/" + d + " SERVICE_SRC_LIST)\n")
                if service["loadType"] == "dynamic":
                    file.write("set(SERVICE_SRC_LIST")
                    for i in files("../../../../usr/impl/" + base_name + "/" + service["name"], "*.cpp"):
                        file.write(" ${PROJECT_SOURCE_DIR}/" + i.replace("\\", "/"))
                    file.write(' ${PROJECT_SOURCE_DIR}/../../../../src/stub/' + base_name + '/' + service[
                        "name"] + '/' + base_name + '.service.' + service["name"] + '.so.cpp')
                    file.write(")\n")
                    file.write("add_library(" + service["name"] + ' SHARED ${SERVICE_SRC_LIST})\n\n')
                    file.write("IF (MSVC)\n")
                    file.write('    set_target_properties(' + service["name"] + ' PROPERTIES COMPILE_FLAGS "/EHa")\n')
                    file.write('ENDIF ()\n')
                    file.write("SET_TARGET_PROPERTIES(" + service["name"] + ' PROPERTIES PREFIX "lib")\n')
                    file.write("SET_TARGET_PROPERTIES(" + service["name"] + ' PROPERTIES SUFFIX ".so")\n')
                else:
                    file.write("set(SERVICE_SRC_LIST")
                    for i in files("../../../../usr/impl/" + base_name + "/" + service["name"], "*.cpp"):
                        file.write(" ${PROJECT_SOURCE_DIR}/" + i.replace("\\", "/"))
                    file.write(")\n")
                    file.write('add_library(' + service["name"] + ' ${SERVICE_SRC_LIST})\n\n')
                    file.write("IF (MSVC)\n")
                    file.write('    set_target_properties(' + service["name"] + ' PROPERTIES COMPILE_FLAGS "/EHa")\n')
                    file.write('ENDIF ()\n')
                    file.write("SET_TARGET_PROPERTIES(" + service["name"] + ' PROPERTIES PREFIX "lib")\n')
                    file.write("SET_TARGET_PROPERTIES(" + service["name"] + ' PROPERTIES SUFFIX ".a")\n')
                if platform.system() == "Windows":
                    file.write('target_link_libraries(' + service["name"] + '\n')
                    file.write(
                        '    debug ${PROJECT_SOURCE_DIR}/../../../../lib/' + base_name + '/Debug/lib' + base_name + '.a optimized ${PROJECT_SOURCE_DIR}/../../../../lib/' + base_name + '/Release/lib' + base_name + '.a\n')
                    file.write(
                        'debug ${PROTOBUF_PATH}/lib/libprotobufd.lib optimized ${PROTOBUF_PATH}/lib/libprotobuf.lib\n')
                else:
                    file.write('target_link_libraries(' + service["name"] + '\n')
                    file.write('    ${PROJECT_SOURCE_DIR}/../../../../lib/' + base_name + '/lib' + base_name + '.a\n')
                    file.write('    -lprotobuf\n')
                file.write(')\n')
                file.close()
                if platform.system() == "Windows":
                    os.system('cmake -G  "Visual Studio 16 2019" -A x64')
                    os.system('cmake --build . --config "Release"')
                    os.system('cmake --build . --config "Debug"')
                else:
                    os.system("cmake .")
                    os.system("make")
            os.chdir("../../")
        os.chdir("../../")

    def addIdl2Repo(self, file_name, sname=None, add=True):
        if not add:
            file_name = "src/idl/" + file_name
        if not os.path.exists(file_name):
            print(file_name + " not found")
            return
        file_name = self.check_idl_name(file_name)
        if add:
            shutil.copyfile(file_name, "src/idl/" + os.path.basename(file_name))
        os.chdir("src/idl/")
        cmd = subprocess.Popen(["../../bin/rpc-frontend", "-f", os.path.basename(file_name)], stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        ret = cmd.communicate()
        if cmd.returncode != 0:
            print(ret[1])
            return
        cmd = subprocess.Popen(["python", "../../bin/cppgen.py", os.path.basename(file_name) + ".cpp.json"],
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        ret = cmd.communicate()
        if cmd.returncode != 0:
            print("cppgen.py failed")
            print(ret[1])
            return
        cmd = subprocess.Popen(["python", "../../bin/cppgen_pb_layer.py", os.path.basename(file_name) + ".cpp.json",
                                os.path.basename(file_name) + ".protobuf.json"], stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        ret = cmd.communicate()
        if cmd.returncode != 0:
            print("cppgen_pb_layer.py failed")
            print(ret[1])
            return
        (base_name, _) = os.path.splitext(os.path.basename(file_name))
        json_str = json.load(open(base_name + ".idl.cpp.json"))
        for service_name in json_str["serviceNames"]:
            if sname is not None:
                if sname != service_name:
                    continue
            if not os.path.exists("../include/" + base_name + "/" + service_name + "/proxy"):
                os.makedirs("../include/" + base_name + "/" + service_name + "/proxy")
            if not os.path.exists("../include/" + base_name + "/" + service_name + "/stub"):
                os.makedirs("../include/" + base_name + "/" + service_name + "/stub")
            if not os.path.exists("../proxy/" + base_name + "/" + service_name):
                os.makedirs("../proxy/" + base_name + "/" + service_name)
            if not os.path.exists("../stub/" + base_name + "/" + service_name):
                os.makedirs("../stub/" + base_name + "/" + service_name)
            if not os.path.exists("../include/" + base_name + "/protobuf"):
                os.makedirs("../include/" + base_name + "/protobuf")
            if not os.path.exists("../" + base_name + "/protobuf"):
                os.makedirs("../" + base_name + "/protobuf")
            if not os.path.exists("../../usr/impl/" + base_name + "/" + service_name):
                os.makedirs("../../usr/impl/" + base_name + "/" + service_name)
            shutil.copyfile(base_name + ".struct.h", "../include/" + base_name + "/" + base_name + ".struct.h")
            shutil.copyfile(base_name + ".enum.h", "../include/" + base_name + "/" + base_name + ".enum.h")
            shutil.copyfile(base_name + ".service." + service_name + ".h",
                            "../include/" + base_name + "/" + base_name + ".service." + service_name + ".h")
            shutil.copyfile(base_name + '.service.' + service_name + ".proxy.h",
                            "../include/" + base_name + "/" + service_name + "/proxy/" + base_name + '.service.' + service_name + ".proxy.h")
            shutil.copyfile(base_name + '.service.' + service_name + ".proxy.serializer.h",
                            "../include/" + base_name + "/" + service_name + "/proxy/" + base_name + '.service.' + service_name + ".proxy.serializer.h")
            shutil.copyfile(base_name + '.service.' + service_name + ".proxy.cpp",
                            "../proxy/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".proxy.cpp")
            shutil.copyfile(base_name + '.service.' + service_name + ".proxy.serializer.cpp",
                            "../proxy/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".proxy.serializer.cpp")
            shutil.copyfile(base_name + '.service.' + service_name + ".stub.h",
                            "../include/" + base_name + "/" + service_name + "/stub/" + base_name + '.service.' + service_name + ".stub.h")
            shutil.copyfile(base_name + '.service.' + service_name + ".stub.serializer.h",
                            "../include/" + base_name + "/" + service_name + "/stub/" + base_name + '.service.' + service_name + ".stub.serializer.h")
            shutil.copyfile(base_name + '.service.' + service_name + ".stub.cpp",
                            "../stub/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".stub.cpp")
            shutil.copyfile(base_name + '.service.' + service_name + ".stub.serializer.cpp",
                            "../stub/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".stub.serializer.cpp")
            if not os.path.exists(
                    "../../usr/impl/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".impl.h"):
                shutil.move(base_name + ".service." + service_name + ".impl.h",
                            "../../usr/impl/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".impl.h")
            else:
                os.remove(base_name + ".service." + service_name + ".impl.h")
            if not os.path.exists(
                    "../../usr/impl/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".cpp"):
                shutil.copyfile(base_name + '.service.' + service_name + ".cpp",
                                "../../usr/impl/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".cpp")
        for service in json_str["services"]:
            if sname is not None:
                if sname != service["name"]:
                    continue
            if service["loadType"] == "dynamic":
                service_name = service["name"]
                shutil.copyfile(base_name + '.service.' + service_name + ".so.h",
                                "../include/" + base_name + "/" + service_name + "/stub/" + base_name + '.service.' + service_name + ".so.h")
                shutil.copyfile(base_name + '.service.' + service_name + ".so.cpp",
                                "../stub/" + base_name + "/" + service_name + "/" + base_name + '.service.' + service_name + ".so.cpp")
            if service['type'] == 'generic':
                if service.has_key("notations"):
                    continue
                is_lua = False
                for notation in service["notations"]:
                    if notation.has_key("script_type"):
                        for script_type in notation["script_type"]:
                            if script_type == "lua":
                                is_lua = True
                if not is_lua:
                    continue
                if not os.path.exists("../../usr/impl/" + base_name + "/" + service['name']):
                    os.makedirs("../../usr/impl/" + base_name + "/" + service['name'])
                shutil.copyfile("../../usr/lua_template/lua_service_template.h",
                                "../../usr/impl/" + base_name + "/" + service[
                                    'name'] + '/' + base_name + '.service.' + service_name + '.h')
                shutil.copyfile("../../usr/lua_template/lua_service_template.cpp",
                                "../../usr/impl/" + base_name + "/" + service[
                                    'name'] + '/' + base_name + '.service.' + service_name + '.cpp')
        shutil.copyfile(base_name + '.service.pb.h',
                        "../include/" + base_name + "/protobuf/" + base_name + '.service.pb.h')
        shutil.copyfile(base_name + '.service.pb.cc', "../" + base_name + "/protobuf/" + base_name + '.service.pb.cc')
        remove_files(".", "*.h")
        remove_files(".", "*.cpp")
        remove_files(".", "*.cc")
        # remove_files(".", "*.proto")
        os.chdir("../../")

    def updateRoot(self):
        initializer = Initializer_cpp(Options())
        initializer.inspectpath()
        os.chdir("tmp/rpc-frontend/")
        os.system("git pull")
        os.chdir("..")
        initializer.build_frontend()
        os.chdir("..")
        os.chdir("tmp/rpc-backend-cpp/")
        os.system("git pull")
        os.chdir("..")
        initializer.build_backend()
        os.chdir("../../")
